package com.ebspp.client;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.ebspp.client.buffer.IBuffer;
import com.ebspp.client.buffer.MvAsyncBuffer;
import com.ebspp.client.serverManager.ServerManager;
import com.ebspp.client.watcher.WatchBuffer;
import com.ebspp.client.watcher.WatchManager;
import com.ebspp.global.General;
import com.ebspp.thrift.Identity;
import com.ebspp.thrift.ResourceType;


/**
 * The core class of the application site EBS++ client
 * It is initiated by the application.
 * Each application can have only one ServerManager, managing the connections and server-side information
 * Each application can have one clients, each identified by an cluster-wide unique id
 * Each client can has multiple bufferpools, each identified by client-wide unique buffer id
 * 
 * each client can provision multiple kind of resource by using different buffer pool
 * 
 * @author x23pan
 *
 */
public class EBSPPClient
{
	//supported buffer type
	public enum BufferType{
		MULTIVERSION_ASYNC
	};

	Identity id;
	//It maintains all its buffers by the buffer ID;
	private Map<Integer, IBuffer> bufMap = null;
	
	public EBSPPClient(Identity id)
	{
		this.id = id;
		bufMap = Collections.synchronizedMap(new HashMap<Integer, IBuffer>());
	}
	
	public Identity getID()
	{
		return id;
	}
	
	/**
	 * create a buffer. 
	 * resType is the resource to be used at server side.
	 * bufType is the buffer type used locally
	 * 
	 * @param resType
	 * @param bufType
	 * @return
	 */
	public IBuffer createRWBuffer(ResourceType resType, BufferType bufType)
	{
		IBuffer buf = null;
		try {
			switch(bufType)
			{
			case MULTIVERSION_ASYNC:
				buf = new MvAsyncBuffer(General.writeBufSize, General.readBufSize, General.wFailBufSize, 
										General.getBufID(), id, resType);
				break;
			default:
				System.err.println("Unsupported buffer type");
				break;
			}

		} 
		catch (Exception e) 
		{
			// TODO Auto-generated catch block
			System.err.println("Fail to create buffer: " + bufType);
			e.printStackTrace();
			return null;
		}
		
		bufMap.put(buf.getBufID(), buf);
		return buf;
	}

	/**
	 * get the buffer by the id, if not exists, return null;
	 * @param type
	 * @return
	 */
	public IBuffer getRWBuffer(int bufID)
	{
		return bufMap.get(bufID);
	}
	
	
	
	public boolean watch(List<String> servers, ResourceType resType, Identity wID)
	{
		if(!WatchManager.isInit())
			WatchManager.initWatch();
		
		return WatchManager.watch(servers, resType, wID);
	}
	public void deWatch(String server, ResourceType resType, Identity wID)
	{
		WatchManager.deWatch(server, resType, wID);
	}
	
	public WatchBuffer getWatchBuffer(ResourceType resType, Identity wID)
	{
		return WatchManager.getBuffer(resType, wID);
	}
	
	
	public void closeRWBuf()
	{
		Iterator<IBuffer> bufs = bufMap.values().iterator();
		synchronized(bufMap)
		{
			while(bufs.hasNext())
			{
				IBuffer buf = bufs.next();
				if(!buf.close())
					System.err.println("Fail to close buffer " + 
										buf.getResType()+":"+buf.getBufID());
			}
		}
	}
	
	public void closeWatchBuf()
	{
		WatchManager.close();
	}
	
}


